package com.study.get.jsl.ths;

import cn.hutool.bloomfilter.BitMapBloomFilter;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONReader.Feature;
import com.alibaba.fastjson2.annotation.JSONField;
import com.google.common.collect.Lists;
import com.study.get.jsl.BeileizhenDemo.TenTool;
import com.study.get.jsl.DataDTO.JslRowCell;
import com.study.get.jsl.DataDTO.KzzDTO;
import com.study.get.jsl.ths.ThsNewsUtils.ThsDataInter.CompareDTO;
import com.study.get.jsl.ths.ThsNewsUtils.ThsDataInter.ThsNewDTO;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * @author zhuzi
 * 2025/3/16 下午5:03
 */
@Slf4j
public class ThsNewsUtils {
    ThreadPoolExecutor executor = new ThreadPoolExecutor(20,      // 核心线程数
            20,      // 最大线程数（与核心线程数相同，确保固定大小）
            0L,       // 空闲线程存活时间（0表示立即回收）
            TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(200),  // 有界队列，避免OOM
            Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy()  // 拒绝策略（可根据需求调整）
    );
    public static BitMapBloomFilter bloomFilter = new BitMapBloomFilter(10);

    @Test
    public void allThsNew() {
        StopWatch watch = StopWatch.createStarted();
        log.info("同花顺消息");
        String body = FileUtil.readUtf8String("/Users/zhuzi/Downloads/kzz-24-06-14.json");
        List<JslRowCell> kzzDTOS = JSON.parseArray(JSON.parseObject(body).getString("rows"), JslRowCell.class, Feature.SupportSmartMatch);

        List<KzzDTO> filterStockList = kzzDTOS.stream().map(ib -> ib.getCell()).filter(ia -> (ia.getYearLeft() != null && NumberUtil.isGreater(ia.getYearLeft(), new BigDecimal("0.5")))//
                && NumberUtil.isLessOrEqual(ia.getCurrIssAmt(), new BigDecimal("8"))).collect(Collectors.toList());
        String titlea = "🎉 Ths消息-最新";
        List<ThsNewDTO> thsNewLists = Lists.newArrayList();
        log.info("过滤之后数量{}", filterStockList.size());


        List<CompletableFuture<List<ThsNewDTO>>> futures = filterStockList.stream()
                .filter(ia -> bloomFilter.add(ia.getStockId()))//不过滤,有些股票2个可转债,统计会翻倍
                .map(ia -> //
                        CompletableFuture.supplyAsync(() -> getThsNewByKzzStock(ia),  // 异步执行任务
                                executor  // 指定线程池
                        )).collect(Collectors.toList());

        List<ThsNewDTO> collect = futures.stream().map(CompletableFuture::join)//
                .filter(CollectionUtils::isNotEmpty).flatMap(List::stream).collect(Collectors.toList());

        String msg = StrUtil.format("{}\n  {}", titlea, collect.stream().map(ThsNewDTO::ding).collect(Collectors.joining("\n")));
        log.info("过滤之后数量{}", filterStockList.size());
        System.out.println(msg);
        System.out.println("耗时(s) " + watch.getTime(TimeUnit.SECONDS));

        //
        Map<String, Long> countByName = collect.stream().collect(Collectors.groupingBy(ThsNewDTO::getName, Collectors.counting()));
        ArrayList<@Nullable CompareDTO> compareList = Lists.newArrayList();
        countByName.forEach((k, v) -> {
            compareList.add(new CompareDTO().setName(k).setCount(v));
        });

        ArrayList<CompareDTO> sortedList = compareList.stream().sorted(Comparator.comparing(CompareDTO::getCount).reversed()).collect(Collectors.toCollection(ArrayList::new));

        ArrayList<String> lineList = Lists.newArrayList("");
        System.out.println("次数大于1次");
        lineList.add("次数大于1次");
        List<CompareDTO> compare3 = sortedList.stream().filter(ib -> ib.getCount() > 1).collect(Collectors.toList());
        for (CompareDTO compareDTO : compare3) {
            System.out.println(compareDTO.getName() + "  " + compareDTO.getCount());
            lineList.add(compareDTO.getName() + "  " + compareDTO.getCount());
        }
        List<CompareDTO> compare1 = sortedList.stream().filter(ib -> ib.getCount() == 1).collect(Collectors.toList());
        String compare1Name = compare1.stream().map(ib -> ib.getName()).collect(Collectors.joining(" "));
        System.out.println("次数为1次");
        lineList.add("次数为1次");
        lineList.add(compare1Name);
        System.out.println(compare1Name);

        System.out.println("======================");
        Map<String, List<ThsNewDTO>> mapByName = collect.stream().collect(Collectors.groupingBy(ThsNewDTO::getName, Collectors.toList()));
        sortedList.forEach(ia -> {
            String name = ia.getName();
            List<ThsNewDTO> thsNewDTOList = mapByName.get(name);
            ia.setList(thsNewDTOList);
        });

        List<CompareDTO> resultList = sortedList.stream().filter(ib -> ib.getCount() >= 2).collect(Collectors.toList());
        resultList.forEach(ia -> {
            List<ThsNewDTO> list = ia.getList();
            String tmpMsg = StrUtil.format("🎉 {} {}次\n {}{}", ia.getName(), ia.getCount(), list.stream().map(ThsNewDTO::dingTotal).collect(Collectors.joining("\n")), StrUtil.repeat("*-", 10));
            System.out.println(tmpMsg);
            lineList.add(tmpMsg);
        });
        FileUtil.writeLines(lineList, "/Users/zhuzi/Downloads/sql/ths/" + LocalDate.now() + RandomUtil.randomString(5) + ".txt", StandardCharsets.UTF_8);
    }

    @Test
    public void allThsNewTag() {
        StopWatch watch = StopWatch.createStarted();
        log.info("同花顺消息");
        String body = FileUtil.readUtf8String("/Users/zhuzi/Downloads/kzz-24-06-14.json");
        List<JslRowCell> kzzDTOS = JSON.parseArray(JSON.parseObject(body).getString("rows"), JslRowCell.class, Feature.SupportSmartMatch);

        List<KzzDTO> filterStockList = kzzDTOS.stream().map(ib -> ib.getCell())//
                .filter(ia -> (ia.getYearLeft() != null && NumberUtil.isGreater(ia.getYearLeft(), new BigDecimal("0.5")))//
                        && NumberUtil.isLessOrEqual(ia.getCurrIssAmt(), new BigDecimal("8")))
                .collect(Collectors.toList());
        String titlea = "🎉 Ths消息-最新";
        List<ThsNewDTO> thsNewLists = Lists.newArrayList();
        log.info("过滤之后数量{}", filterStockList.size());


        List<CompletableFuture<List<ThsNewDTO>>> futures = filterStockList.stream()
                .filter(ia -> bloomFilter.add(ia.getStockId()))//不过滤,有些股票2个可转债,统计会翻倍
                .map(ia -> //
                        CompletableFuture.supplyAsync(() -> getThsNewByKzzStockTag(ia),  // 异步执行任务
                                executor  // 指定线程池
                        )).collect(Collectors.toList());

        List<ThsNewDTO> collect = futures.stream().map(CompletableFuture::join)//
                .filter(CollectionUtils::isNotEmpty).flatMap(List::stream).collect(Collectors.toList());

        String msg = StrUtil.format("{}\n  {}", titlea, collect.stream().map(ThsNewDTO::ding).collect(Collectors.joining("\n")));
        log.info("过滤之后数量{}", filterStockList.size());
        System.out.println(msg);
        System.out.println("耗时(s) " + watch.getTime(TimeUnit.SECONDS));


        FileUtil.writeUtf8String(msg, "/Users/zhuzi/Downloads/sql/ths/" + LocalDate.now() + RandomUtil.randomString(5) + ".txt");
    }

    /**
     * 抓取通同花顺消息
     *
     * @param kzzStock
     * @return
     */
    private List<ThsNewDTO> getThsNewByKzzStock(KzzDTO kzzStock) {
        ThreadUtil.safeSleep(RandomUtil.randomInt(500, 1500));
        String thsNewUrl = "https://flow.10jqka.com.cn/pc/timeline/v1/base/news/{}/{}/first/0";
        String urlTmp = StrUtil.startWith(kzzStock.getBondId(), "11") ? StrUtil.format(thsNewUrl, "17", kzzStock.getStockId()) : StrUtil.format(thsNewUrl, "33", kzzStock.getStockId());
        String block = TenTool.webClient.get().uri(urlTmp).header("Accept", "*/*").header("User-Agent", "tong hua shun/4.6.0 (Mac OS X ban ben12.7.6(ban hao21H1320))").retrieve().bodyToMono(String.class)//
                .timeout(Duration.ofSeconds(8)).retry(3).block();
        if (StrUtil.isNotBlank(block)) {
            JSONObject jsonObject = JSON.parseObject(block);
            String string = jsonObject.getString("data");
            List<ThsNewDTO> thsNewList = JSON.parseArray(string, ThsNewDTO.class, Feature.SupportSmartMatch, Feature.SupportAutoType);
            thsNewList.forEach(iaa -> {
                iaa.setName(kzzStock.getStockNm());
                iaa.setCode(kzzStock.getStockId());
            });
            // 根据时间和内容过滤
            return thsNewList.stream().filter(ThsNewDTO::predicateLately).filter(ThsNewDTO::predicateContext).collect(Collectors.toList());
        }
        return Lists.newArrayList();
    }

    private List<ThsNewDTO> getThsNewByKzzStockTag(KzzDTO kzzStock) {
        ThreadUtil.safeSleep(RandomUtil.randomInt(500, 1500));
        String thsNewUrl = "https://flow.10jqka.com.cn/pc/timeline/v1/base/news/{}/{}/first/0";
        String urlTmp = StrUtil.startWith(kzzStock.getBondId(), "11") ? StrUtil.format(thsNewUrl, "17", kzzStock.getStockId()) : StrUtil.format(thsNewUrl, "33", kzzStock.getStockId());
        String block = TenTool.webClient.get().uri(urlTmp).header("Accept", "*/*").header("User-Agent", "tong hua shun/4.6.0 (Mac OS X ban ben12.7.6(ban hao21H1320))").retrieve().bodyToMono(String.class)//
                .timeout(Duration.ofSeconds(8)).retry(3).block();
        if (StrUtil.isNotBlank(block)) {
            JSONObject jsonObject = JSON.parseObject(block);
            String string = jsonObject.getString("data");
            List<ThsNewDTO> thsNewList = JSON.parseArray(string, ThsNewDTO.class, Feature.SupportSmartMatch, Feature.SupportAutoType);
            thsNewList.forEach(iaa -> {
                iaa.setName(kzzStock.getStockNm());
                iaa.setCode(kzzStock.getStockId());
            });
            // 根据时间和内容过滤
            return thsNewList.stream().filter(ThsNewDTO::predicate).filter(ThsNewDTO::predicateContext).collect(Collectors.toList());
        }
        return Lists.newArrayList();
    }

    public interface ThsDataInter {

        @Data
        @Accessors(chain = true)
        class ThsNewDTO {
            private String code;
            private String name;
            private String itemId;
            private String type;
            private String jumpUrl;
            private String title;
            private Long time;
            private String date;
            private String tag;
            @JSONField(name = "abstract")
            private String detail;

            private LocalDateTime pubTime;
            private LocalDate pubDate;

            public static boolean predicate(ThsNewDTO dto) {
                return Objects.nonNull(dto) &&//
                        StrUtil.equals("置顶", dto.tag);
            }

            public static boolean predicateLately(ThsNewDTO dto) {
                return Objects.nonNull(dto) &&//
                        !StrUtil.equals("置顶", dto.tag) && dto.getPubDate().equals(LocalDate.now());
            }

            /**
             * 基于内容过滤
             *
             * @param dto 同花顺消息
             * @return
             */
            public static boolean predicateContext(ThsNewDTO dto) {
                ArrayList<String> contList = Lists.newArrayList("将迎主升浪", "融资买入", "主力大幅", "一字跌停", "技术分析", "大宗交易", "龙虎榜"//
                        , "股东人数", "股东户数", "技术面", "聘任", "股东会", "股东大会", "减持");
                String[] arr = new String[]{"将迎主升浪", "融资买入", "主力大幅", "一字跌停", "技术分析", "大宗交易", "龙虎榜"//
                        , "股东人数", "股东户数", "技术面", "聘任", "股东会", "股东大会", "减持", "WR下破2", "RSI", "短线", "多头排列", "千股千评",//
                        "次回测数据", "技术角度", "主力", "空中加油", "基金调研", "形态分析", "K线", "调研活动",//
                        "调研我司", "转股价格", "技术形态", "收评", "【观点", "复盘", "机构评", "千股千评", "人气暴涨", "股价突破"//
                        , "股东持有人", "次数据回测", "系列点评", "解读行情", "回购进展", "技术指标", "个股情绪", "对外担保", "被担保公司", "封死跌停"//
                        , "价格汇总", "现金管理", "箱体震荡", "三连阳", "下降通道", "现货】", "BBI", "BBI均线", "布林线", "MACD", "价格前线"//
                        , "周报", "历史新高", "继续上涨", "低位震荡", "CDFI", "下降通道", "并购", "视察", "公司股价波动", "一阳穿三线"//
                        , "股票激励", "股权激励", "回购"
                };
                //return !StrUtil.containsAny(dto.getTitle(), contList.toArray(new String[contList.size()]));
                return !StrUtil.containsAny(dto.getTitle(), arr);
            }


            public String ding() {
                //松原转债223.56|20|2.28 |差17.72
                return StrUtil.format("🏆 {}\n📌{}\n{}\n{}\n{}", name, title, pubTime, jumpUrl, StrUtil.repeat("*-", 10));
            }

            /**
             * 根据股票名称汇总输出
             *
             * @return
             */
            public String dingTotal() {
                //松原转债223.56|20|2.28 |差17.72
                return StrUtil.format("📌{}\n{}\n{}\n\n", title, pubTime, jumpUrl);
            }


            public ThsNewDTO setTime(Long time) {
                this.time = time;
                if (Objects.nonNull(time)) {
                    this.pubTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(time * 1000), ZoneId.systemDefault());
                    this.pubDate = this.pubTime.toLocalDate();
                }
                return this;
            }
        }

        @Data
        @Accessors(chain = true)
        class CompareDTO {
            private String name;
            private Long count;
            List<ThsNewDTO> list;
        }
    }
}
